import pystray
from PIL import Image, ImageDraw
import threading
import asyncio
import os
import sys
import subprocess
import winreg
from win32com.client import Dispatch
from ..config import Config
from ..websocket_client import WebSocketClient
from ..utils.logger import get_logger
import traceback
import json
import tkinter as tk
from tkinter import messagebox

logger = get_logger(__name__)

class SystemTray:
    def __init__(self, config: Config):
        self.config = config
        self.ws_client = None
        self.loop = None
        self.icon = None
        self._icon_image = None  # 缓存图标
        self._menu_lock = threading.Lock()  # 添加菜单锁
        self._root = tk.Tk()  # 创建主窗口
        self._root.withdraw()  # 隐藏主窗口
        
        # 注册应用程序
        if sys.platform == 'win32':
            self._register_app()
        
        try:
            logger.info("Creating system tray icon...")
            self.icon = pystray.Icon(
                name=config.APP_NAME,
                icon=self._get_icon(),
                title=config.APP_NAME,
                menu=self._create_menu('状态: 未连接')
            )
            logger.info("System tray icon created successfully")
            
            # 检查通知权限
            if not self._check_notification_permissions():
                messagebox.showwarning(
                    "通知权限提醒",
                    "检测到通知权限未开启，建议开启通知权限以获得最佳体验。\n"
                    "您可以稍后在菜单中点击【权限设置】进行设置。"
                )
        except Exception as e:
            logger.error(f"Failed to create system tray: {e}")
            raise
    
    def _register_app(self):
        """注册应用程序到 Windows"""
        try:
            # 设置应用程序 ID
            import ctypes
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(self.config.APP_ID)
            
            # 创建快捷方式（用于 Windows 识别应用程序）
            shortcut_path = os.path.join(
                os.environ["APPDATA"],
                "Microsoft\\Windows\\Start Menu\\Programs",
                f"{self.config.APP_NAME}.lnk"
            )
            
            # 确保目录存在
            os.makedirs(os.path.dirname(shortcut_path), exist_ok=True)
            
            # 删除旧的快捷方式（如果存在）
            if os.path.exists(shortcut_path):
                os.remove(shortcut_path)
            
            # 创建新的快捷方式
            shell = Dispatch('WScript.Shell')
            shortcut = shell.CreateShortCut(shortcut_path)
            shortcut.TargetPath = sys.executable
            shortcut.IconLocation = self.config.TRAY_ICON_PATH
            shortcut.WorkingDirectory = os.path.dirname(sys.executable)
            shortcut.Description = self.config.APP_NAME
            shortcut.Save()
            
            # 注册应用程序到注册表
            try:
                # 删除旧的注册表项（如果存在）
                try:
                    winreg.DeleteKey(winreg.HKEY_CURRENT_USER, f"Software\\Classes\\AppUserModelId\\{self.config.APP_ID}")
                except WindowsError:
                    pass
                try:
                    winreg.DeleteKey(winreg.HKEY_CURRENT_USER, f"Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings\\{self.config.APP_ID}")
                except WindowsError:
                    pass

                # 注册应用程序标识
                app_key_path = f"Software\\Classes\\AppUserModelId\\{self.config.APP_ID}"
                with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, app_key_path, 0, winreg.KEY_WRITE) as key:
                    winreg.SetValueEx(key, "DisplayName", 0, winreg.REG_SZ, self.config.APP_NAME)
                    winreg.SetValueEx(key, "IconUri", 0, winreg.REG_SZ, self.config.TRAY_ICON_PATH)
                    winreg.SetValueEx(key, "IconBackgroundColor", 0, winreg.REG_SZ, "(0, 120, 215)")
                    winreg.SetValueEx(key, "SmallIconBackgroundColor", 0, winreg.REG_SZ, "(0, 120, 215)")
                    winreg.SetValueEx(key, "SystemSettingsExperience", 0, winreg.REG_SZ, "Microsoft.Windows.Notifications")
                    winreg.SetValueEx(key, "Version", 0, winreg.REG_DWORD, 1)
                    winreg.SetValueEx(key, "ShowInSettings", 0, winreg.REG_DWORD, 1)

                # 注册通知设置
                settings_key_path = f"Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings\\{self.config.APP_ID}"
                with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, settings_key_path, 0, winreg.KEY_WRITE) as key:
                    winreg.SetValueEx(key, "ShowInActionCenter", 0, winreg.REG_DWORD, 1)
                    winreg.SetValueEx(key, "Enabled", 0, winreg.REG_DWORD, 1)
                    winreg.SetValueEx(key, "ShowInSettings", 0, winreg.REG_DWORD, 1)
                    winreg.SetValueEx(key, "Version", 0, winreg.REG_DWORD, 1)

            except Exception as e:
                logger.error(f"Failed to register app in registry: {e}")
                raise
                
            logger.info("Application registered successfully")
        except Exception as e:
            logger.error(f"Failed to register application: {e}")
            raise
    
    def _create_icon(self):
        """创建默认图标或加载自定义图标"""
        try:
            if os.path.exists(self.config.TRAY_ICON_PATH):
                # 加载并调整图标大小
                image = Image.open(self.config.TRAY_ICON_PATH)
                # 确保图像是 RGBA 模式
                if image.mode != 'RGBA':
                    image = image.convert('RGBA')
                # 调整大小使用新的重采样方法
                image = image.resize((22, 22), Image.Resampling.LANCZOS)
                return image
        except Exception as e:
            logger.warning(f"Failed to load icon: {e}")
        
        # 创建一个默认的图标
        image = Image.new('RGBA', (22, 22), color=(0, 0, 0, 0))
        dc = ImageDraw.Draw(image)
        # 绘制一个简单的圆形图标
        dc.ellipse([2, 2, 20, 20], fill='blue')
        return image
    
    def _get_icon(self):
        """获取图标（带缓存）"""
        if self._icon_image is None:
            self._icon_image = self._create_icon()
        return self._icon_image
    
    def _create_menu(self, status_text: str = '状态: 未连接'):
        """创建菜单"""
        return pystray.Menu(
            pystray.MenuItem(
                f"{status_text}",
                lambda: None,
                enabled=False
            ),
            pystray.MenuItem(
                "测试通知",
                self._test_notification
            ),
            pystray.MenuItem(
                "权限设置",
                self._open_notification_settings
            ),
            pystray.MenuItem(
                "Token设置",
                self._open_account_settings
            ),
            pystray.MenuItem(
                "重新连接",
                self._reconnect
            ),
            pystray.MenuItem(
                "退出",
                self._quit
            )
        )
    
    def _reconnect(self):
        """重新连接"""
        try:
            # 检查是否有账户配置
            if not os.path.exists(self.config.ACCOUNT_PATH):
                logger.info("No account settings found, opening account dialog")
                self._open_account_settings()
                return

            # 有账户配置，执行重连
            if self.ws_client:
                self.ws_client.stop()
            self.start_websocket()
        except Exception as e:
            logger.error(f"Failed to reconnect: {e}")
    
    def _quit(self):
        """退出应用程序"""
        try:
            if messagebox.askokcancel(
                "确认退出",
                "确定要退出程序吗？\n"
                "退出后将不再接收新消息通知。"
            ):
                # 显示退出提示
                messagebox.showinfo(
                    "退出提示",
                    "正在断开与服务端的连接，释放资源后将自动关闭任务栏，请稍等！"
                )
                
                # 先停止 WebSocket
                if self.ws_client:
                    self.ws_client.stop()
                
                # 停止托盘图标
                if self.icon:
                    self.icon.stop()
        except Exception as e:
            logger.error(f"Error during shutdown: {e}")
        finally:
            os._exit(0)
    
    def start_websocket(self):
        """启动 WebSocket 连接"""
        try:
            if self.ws_client:
                self.ws_client.stop()
            
            self.ws_client = WebSocketClient(self.config)
            self.loop = asyncio.new_event_loop()
            
            # 设置事件循环引用和状态回调
            self.ws_client.set_loop(self.loop)
            self.ws_client.set_status_callback(self.update_connection_status)
            
            def run_websocket():
                asyncio.set_event_loop(self.loop)
                try:
                    self.loop.run_until_complete(self.ws_client.connect())
                except Exception as e:
                    logger.error(f"WebSocket error in thread: {e}")
                    self.update_connection_status(False)
            
            thread = threading.Thread(target=run_websocket, daemon=True)
            thread.start()
            
        except Exception as e:
            logger.error(f"Failed to start WebSocket: {e}")
            self.update_connection_status(False)
    
    def _test_notification(self):
        """发送测试通知"""
        try:
            if not self._check_notification_permissions():
                if messagebox.askyesno(
                    "通知权限未开启",
                    "检测到通知权限未开启，是否现在开启？"
                ):
                    self._open_notification_settings()
                    return
                    
            if self.ws_client and self.ws_client.notifier:
                self.ws_client.notifier.notify(
                    title="测试通知",
                    message="如果您看到这条消息，说明通知系统工作正常!"
                )
                logger.info("发送测试通知")
        except Exception as e:
            logger.error(f"发送测试通知失败: {e}")
            messagebox.showerror("错误", "发送测试通知失败，请检查系统设置。")
    
    def _open_notification_settings(self):
        """打开系统通知设置"""
        try:
            if sys.platform == 'win32':
                # 重新注册应用程序
                try:
                    self._register_app()
                except Exception as e:
                    logger.error(f"Failed to register app before opening settings: {e}")
                
                # 显示更详细的引导信息
                messagebox.showinfo(
                    "通知权限设置",
                    "请按以下步骤开启通知权限：\n\n"
                    "1. 在打开的 Windows 设置中，找到【系统】\n"
                    "2. 点击【通知和操作】\n"
                    f"3. 在应用列表中找到【{self.config.APP_NAME}】\n"
                    "4. 将开关设置为【开启】\n\n"
                    "如果在列表中找不到应用，请尝试：\n"
                    "1. 关闭设置页面\n"
                    "2. 重启应用程序\n"
                    "3. 重新打开设置\n\n"
                    "完成设置后，请点击【确定】继续。"
                )
                
                # 打开 Windows 通知设置
                subprocess.run(['start', 'ms-settings:notifications'], shell=True)
                
                # 等待用户完成设置
                if messagebox.askyesno(
                    "确认设置",
                    "您是否已完成通知权限设置？\n\n"
                    "如果已完成，请点击【是】进行测试\n"
                    "如果还没完成，请点击【否】继续设置"
                ):
                    # 重新注册应用程序
                    self._register_app()
                    # 测试通知
                    self._test_notification()
        except Exception as e:
            logger.error(f"打开通知设置失败: {e}")
            messagebox.showerror(
                "错误",
                f"打开通知设置失败: {e}\n\n"
                "请尝试以下步骤：\n"
                "1. 重启应用程序\n"
                "2. 以管理员身份运行应用程序\n"
                "3. 手动打开 Windows 设置 > 系统 > 通知和操作"
            )
    
    def _open_account_settings(self):
        """打开账户设置对话框"""
        try:
            from .account_dialog import AccountDialog
            dialog = AccountDialog(callback=self._on_account_saved)
            dialog.show()
        except Exception as e:
            logger.error(f"Failed to open account settings: {e}")
    
    def _on_account_saved(self):
        """账户设置保存后的回调"""
        try:
            # 检查当前连接状态
            if not (self.ws_client and self.ws_client.is_connected):
                logger.info("Account saved, starting new WebSocket connection")
                self.start_websocket()
        except Exception as e:
            logger.error(f"Failed to handle account saved: {e}")
    
    def update_connection_status(self, connected: bool):
        """更新连接状态显示"""
        try:
            if not self.icon:
                return
                
            status_text = "状态: 已连接" if connected else "状态: 未连接"
            logger.info(f"Updating connection status to: {status_text}")
            
            def update_icon():
                try:
                    # 直接创建新菜单
                    self.icon.menu = self._create_menu(status_text)
                    # 更新托盘标题
                    self.icon.title = f"{self.config.APP_NAME} - {status_text}"
                except Exception as e:
                    logger.error(f"Failed to update icon: {e}")

            # 确保在主线程中执行
            if threading.current_thread() is threading.main_thread():
                update_icon()
            else:
                # 使用事件循环的线程安全方法
                if self.loop and self.loop.is_running():
                    self.loop.call_soon_threadsafe(update_icon)
                else:
                    timer = threading.Timer(0.1, update_icon)
                    timer.daemon = True
                    timer.start()

        except Exception as e:
            logger.error(f"Error updating connection status: {e}", exc_info=True)
    
    def run(self):
        try:
            logger.info("Running system tray...")
            if self.icon:
                # 检查是否有有效的账户配置
                if self._has_valid_account():
                    logger.info("Valid account found, starting WebSocket client...")
                    self.start_websocket()
                else:
                    logger.info("No valid account settings found, waiting for configuration")
                self.icon.run()
            else:
                logger.error("Icon not initialized")
        except Exception as e:
            logger.error(f"Error running system tray: {e}")
            logger.error(traceback.format_exc())
            raise
    
    def _has_valid_account(self) -> bool:
        """检查是否有有效的账户配置"""
        try:
            if os.path.exists(self.config.ACCOUNT_PATH):
                with open(self.config.ACCOUNT_PATH, 'r', encoding='utf-8') as f:
                    settings = json.load(f)
                    return bool(settings.get('token'))  # 检查 token 是否存在
            return False
        except Exception as e:
            logger.error(f"Error checking account settings: {e}")
            return False
    
    def _check_notification_permissions(self):
        """检查通知权限状态"""
        try:
            if sys.platform == 'win32':
                # 检查应用程序是否正确注册
                app_key_path = f"Software\\Classes\\AppUserModelId\\{self.config.APP_ID}"
                try:
                    app_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, app_key_path, 0, winreg.KEY_READ)
                    winreg.CloseKey(app_key)
                except WindowsError:
                    # 如果应用程序未注册，重新注册
                    logger.info("Application not registered, re-registering...")
                    self._register_app()

                # 检查通知设置
                settings_key_path = f"Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings\\{self.config.APP_ID}"
                try:
                    with winreg.OpenKey(winreg.HKEY_CURRENT_USER, settings_key_path, 0, winreg.KEY_READ) as key:
                        enabled = winreg.QueryValueEx(key, "Enabled")[0]
                        return enabled == 1
                except WindowsError:
                    # 如果键不存在，尝试创建
                    try:
                        with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, settings_key_path, 0, winreg.KEY_WRITE) as key:
                            winreg.SetValueEx(key, "Enabled", 0, winreg.REG_DWORD, 1)
                            winreg.SetValueEx(key, "ShowInActionCenter", 0, winreg.REG_DWORD, 1)
                            winreg.SetValueEx(key, "ShowInSettings", 0, winreg.REG_DWORD, 1)
                            winreg.SetValueEx(key, "Version", 0, winreg.REG_DWORD, 1)
                        return True
                    except Exception as e:
                        logger.error(f"Failed to create notification settings: {e}")
                        return False
            return True
        except Exception as e:
            logger.error(f"检查通知权限失败: {e}")
            return False 